# filepath: d:\CompanyProject\Gitee\DBCompareX\backend\app\models\task_data_sync\scheduler.py
import asyncio
from typing import Dict, Any, List
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.triggers.cron import CronTrigger
import logging
import re

from ...models.task_data_sync.setting import TaskDataSyncSettingModel
from ...models.task_data_sync.execution import TaskDataSyncExecutionModel
from ...utils.cron_utils import convert_quartz_to_apscheduler, parse_cron_expression

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class TaskScheduler:
    """任务调度管理器，负责启动、停止和管理定时任务"""
    
    _instance = None
    _scheduler = None
    _job_mapping = {}  # 任务ID到作业ID的映射
    _is_initialized = False
    
    def __new__(cls):
        """单例模式"""
        if cls._instance is None:
            cls._instance = super(TaskScheduler, cls).__new__(cls)
        return cls._instance
    
    def _init(self):
        """初始化调度器"""
        if not self._is_initialized:
            logger.info("开始初始化任务调度管理器")
            self._scheduler = AsyncIOScheduler(
                jobstores={'default': MemoryJobStore()}
            )
            self._job_mapping = {}
            self._is_initialized = True
            logger.info("任务调度管理器初始化完成")
        else:
            logger.info("任务调度管理器已经初始化，跳过")
    
    async def start(self):
        """启动调度器并加载所有启用状态的任务"""
        # 确保初始化
        self._init()
        
        if not self._scheduler.running:
            try:
                self._scheduler.start()
                logger.info("任务调度器已启动")
                await self.load_active_tasks()
            except Exception as e:
                logger.error(f"启动任务调度器失败: {str(e)}", exc_info=True)
                raise
        else:
            logger.info("任务调度器已经在运行中")
    
    def stop(self):
        """停止调度器"""
        if self._scheduler and self._scheduler.running:
            try:
                self._scheduler.shutdown()
                logger.info("任务调度器已停止")
            except Exception as e:
                logger.error(f"停止任务调度器失败: {str(e)}", exc_info=True)
    
    async def load_active_tasks(self):
        """加载所有启用状态的任务"""
        try:
            tasks = await TaskDataSyncSettingModel.find_active_tasks()
            logger.info(f"找到 {len(tasks)} 个活动任务")
            
            for task in tasks:
                await self.add_task(task)
            
            logger.info(f"已加载 {len(tasks)} 个活动任务到调度器")
        except Exception as e:
            logger.error(f"加载活动任务失败: {str(e)}", exc_info=True)
    
    async def add_task(self, task: Dict[str, Any]):
        """
        添加任务到调度器
        
        Args:
            task: 任务配置信息
        """
        task_id = task["_id"]
        cron_expression = task["cron_expression"]
        status = task["status"]
        
        # 如果任务已存在，先移除
        await self.remove_task(task_id)
        
        # 只有当状态为启用时才添加任务
        if status != 1:
            logger.info(f"任务 {task_id} 未启用，不添加到调度器")
            return
        
        try:
            # 创建cron触发器 - 使用增强版cron表达式处理
            trigger = self._create_cron_trigger_from_expression(cron_expression)
            if not trigger:
                logger.error(f"无法识别的cron表达式格式: {cron_expression}")
                return
                
            # 添加任务
            job = self._scheduler.add_job(
                self._execute_task,
                trigger=trigger,
                args=[task_id],
                id=f"task_{task_id}",
                replace_existing=True,
                misfire_grace_time=3600  # 允许错过的任务在一小时内仍然执行
            )
            
            # 记录任务映射
            self._job_mapping[task_id] = job.id
            
            logger.info(f"已添加任务 {task_id} 到调度器，下次执行时间: {job.next_run_time}")
        except Exception as e:
            logger.error(f"添加任务 {task_id} 时出错: {str(e)}", exc_info=True)
    
    def _create_cron_trigger_from_expression(self, cron_expression: str):
        """
        根据任何格式的cron表达式创建触发器，支持Quartz和标准cron格式
        
        Args:
            cron_expression: cron表达式
            
        Returns:
            CronTrigger对象
        """
        try:
            # 首先尝试转换为APScheduler格式
            apscheduler_expression = convert_quartz_to_apscheduler(cron_expression)
            logger.info(f"转换cron表达式: {cron_expression} => {apscheduler_expression}")
            
            # 解析cron表达式
            cron_parts = parse_cron_expression(apscheduler_expression)
            
            # 创建触发器
            if "second" in cron_parts:
                # 6字段格式 (秒 分 时 日 月 周)
                return CronTrigger(
                    second=cron_parts["second"],
                    minute=cron_parts["minute"],
                    hour=cron_parts["hour"],
                    day=cron_parts["day"],
                    month=cron_parts["month"],
                    day_of_week=cron_parts["day_of_week"]
                )
            else:
                # 5字段格式 (分 时 日 月 周)
                return CronTrigger(
                    minute=cron_parts["minute"],
                    hour=cron_parts["hour"],
                    day=cron_parts["day"],
                    month=cron_parts["month"],
                    day_of_week=cron_parts["day_of_week"]
                )
        except Exception as e:
            logger.error(f"解析cron表达式失败: {str(e)}", exc_info=True)
            return None
    
    async def remove_task(self, task_id: str):
        """
        从调度器中移除任务
        
        Args:
            task_id: 任务ID
        """
        if task_id in self._job_mapping:
            try:
                job_id = self._job_mapping[task_id]
                self._scheduler.remove_job(job_id)
                del self._job_mapping[task_id]
                logger.info(f"已移除任务 {task_id} 从调度器")
            except Exception as e:
                logger.error(f"移除任务 {task_id} 时出错: {str(e)}", exc_info=True)
    
    async def update_task(self, task: Dict[str, Any]):
        """
        更新任务配置
        
        Args:
            task: 任务配置信息
        """
        logger.info(f"更新任务 {task['_id']} 的调度配置")
        await self.add_task(task)
    
    async def _execute_task(self, task_id: str):
        """
        执行任务的包装方法
        
        Args:
            task_id: 任务ID
        """
        logger.info(f"调度器触发任务 {task_id} 执行")
        try:
            # 创建异步任务并等待执行完成
            result = await TaskDataSyncExecutionModel.execute_task(task_id)
            logger.info(f"调度器任务 {task_id} 执行完成，批次号: {result['execution_log']['batch_no']}")
        except Exception as e:
            logger.error(f"调度器任务 {task_id} 执行失败: {str(e)}", exc_info=True)
    
    def get_job_status(self, task_id: str) -> Dict[str, Any]:
        """
        获取任务状态
        
        Args:
            task_id: 任务ID
            
        Returns:
            任务状态信息
        """
        if task_id not in self._job_mapping:
            logger.debug(f"任务 {task_id} 未在调度器中找到")
            return {
                "is_scheduled": False,
                "next_run_time": None
            }
        
        job_id = self._job_mapping[task_id]
        job = self._scheduler.get_job(job_id)
        
        if not job:
            logger.warning(f"任务 {task_id} 映射存在但作业不存在")
            return {
                "is_scheduled": False,
                "next_run_time": None
            }
        
        return {
            "is_scheduled": True,
            "next_run_time": job.next_run_time.isoformat() if job.next_run_time else None
        }
    
    def get_scheduler_status(self) -> Dict[str, Any]:
        """
        获取调度器状态
        
        Returns:
            调度器状态信息
        """
        running = self._scheduler and self._scheduler.running
        job_count = len(self._job_mapping)
        
        logger.info(f"调度器状态: running={running}, job_count={job_count}")
        
        return {
            "running": running,
            "job_count": job_count
        }
    
    def list_all_jobs(self) -> List[Dict[str, Any]]:
        """
        列出所有作业
        
        Returns:
            作业列表
        """
        if not self._scheduler:
            return []
            
        jobs = []
        for job in self._scheduler.get_jobs():
            jobs.append({
                "id": job.id,
                "next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
                "trigger": str(job.trigger)
            })
        
        logger.info(f"当前调度器中的作业: {jobs}")
        return jobs

# 全局单例，可以通过TaskScheduler()获取
scheduler = TaskScheduler()

# 启动调度器的辅助函数
async def start_scheduler():
    logger.info("开始启动任务调度器")
    await scheduler.start()
    logger.info(f"任务调度器启动后状态: {scheduler.get_scheduler_status()}")
    logger.info(f"任务调度器中的作业: {scheduler.list_all_jobs()}")

# 停止调度器的辅助函数
def stop_scheduler():
    logger.info("停止任务调度器")
    scheduler.stop()